package com.budayi.common.config;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.budayi.commom.annotation.JsonParam;

/**
 * @author LIBOFEI
 * @ClassName JsonParamProvider
 * @description
 * @date 2021/11/3 17:00
 * @Version 1.0
 **/
public class JsonParamProvider implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(JsonParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
        String json = reader.readLine();
        reader.close();
        JsonParam jsonParam = parameter.getParameterAnnotation(JsonParam.class);
        String paramName = jsonParam.value();
        String param = json;
        if(!"".equals(paramName)){
            JSONObject jsonObject;
            try{
                jsonObject = JSONObject.parseObject(json);
            }catch (Exception e){
                throw new IllegalArgumentException("json格式有误：" + e.getMessage());
            }
            if(jsonParam.require() && (jsonObject == null || jsonObject.get(paramName) == null)){
                throw new IllegalArgumentException("参数缺失：Unknown parameter  ["+ jsonParam.value() +"]");
            }
            param = jsonObject.getString(paramName);
        }
        parameter = parameter.nestedIfOptional();
        String paramTypeClassName = parameter.getParameterType().getSimpleName();
        Type type = parameter.getNestedGenericParameterType();
        Class<?> contextClass = parameter.getContainingClass();
        Class targetClass = (type instanceof Class ? (Class) type : null);
        if (targetClass == null) {
            ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
            targetClass = resolvableType.resolve();
        }
        ObjectMapper mapper = new ObjectMapper();
        TypeFactory typeFactory = mapper.getTypeFactory();
        JavaType javaType = typeFactory.constructType(GenericTypeResolver.resolveType(type, contextClass));
        try{
            if(null == param){
                return param;
            }
            if("String".equals(paramTypeClassName)){
                return param;
            }else if("Date".equals(paramTypeClassName)){
                if(null == jsonParam.datePattern()){
                    throw new  IllegalArgumentException("datePattern not found");
                }
                SimpleDateFormat d = new SimpleDateFormat(jsonParam.datePattern());
                try {
                    Date date = d.parse(param);
                    return date;
                } catch (ParseException e) {
                    throw new  IllegalArgumentException("参数【" + jsonParam.value() + "】格式错误");
                }
            }else if("BigDecimal".equals(paramTypeClassName)){
                try{
                    BigDecimal bigDecimal = new BigDecimal(param);
                    return bigDecimal;
                }catch (Exception e){
                    throw new  IllegalArgumentException("参数【" + jsonParam.value() + "】格式错误");
                }
            }
            Object o = mapper.readerWithView(targetClass).forType(javaType).readValue(param);
            return o;
        }catch (JsonParseException e){
            throw new IllegalArgumentException("参数类型转换错误：parameter type mistake ["+ e.getMessage() +"]");
        }
    }
}
